/*
 * Licensed to the Tai Ping Jin Ke
 *
 * Copyright (c) 2022 .
 * All rights reserved.
 * 项目名称：太平金科-基础组件封装层-会话管理-核心依赖
 * 版权说明：本软件属太平金融科技服务(上海)有限公司所有，在未获得太平金融科技服务(上海)有限公司正式授权情况下，任何企业和个人，不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。
 */
package com.taipingframework.boot.session.support;

import cn.hutool.core.lang.UUID;
import com.taipingframework.boot.session.constant.SessionConstant;
import com.taipingframework.boot.web.response.status.ExceptionStatusEnum;
import com.taipingframework.utility.exception.SystemInterrupterException;
import com.taipingframework.utility.exception.UtilityException;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import org.apache.commons.lang3.StringUtils;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Map;

/**
 * 发送HTTP请求的客户端详细信息
 */
@NoArgsConstructor
@Getter
@Setter
@EqualsAndHashCode
@ToString
public final class ClientDetails implements Serializable {

    /**
     * 唯一标识可序列化的类
     */
    private static final Long serialVersionUID = -1693602165749L;

    /**
     * 来自客户端的HTTP请求线程唯一标识
     */
    private String clientRequestId;
    /**
     * 客户端的登录名
     */
    private String clientLoginName;
    /**
     * 客户端的IP地址
     */
    private String clientIpAddress;
    /**
     * 客户端的用户空间
     */
    private String clientUserProfile;

    /**
     * 客户端在服务器缓存的访问凭证
     * <p>
     * client-unexpired缓存路径的数据：设备IP-用户名-用户空间 --->> Access-Token
     */
    private String clientPresentToken;
    /**
     * 发送HTTP请求的客户端在服务器已登录的用户列表
     * <p>
     * client-profile缓存路径的数据：设备IP --->> Map<用户空间标识, 用户名>
     */
    private Map<String, String> serverUserProfileMap;
    /**
     * 当前用户已登录的设备列表
     * <p>
     * client-credentials缓存路径的数据：用户名 --->> Map<Access-Token, 设备IP>
     */
    private Map<String, String> serverCredentialsMap;

    /**
     * 请求头携带的访问凭证
     */
    private String requestHeaderToken;
    /**
     * 当前会话绑定的用户名
     */
    private String sessionPrincipal;
    /**
     * 当前会话绑定的客户端IP地址
     */
    private String sessionClientIpAddress;
    /**
     * 当前会话绑定的用户空间
     */
    private String sessionUserProfile;

    /**
     * 创建与读取本地会话的参数验证
     */
    public static String validateForSession(ClientDetails clientDetails) throws UtilityException {
        try {
            // 客户端的登录名
            String clientLoginName = clientDetails.getClientLoginName();
            // 客户端的IP地址
            String clientIpAddress = clientDetails.getClientIpAddress();
            // 客户端的用户空间
            String clientUserProfile = clientDetails.getClientUserProfile();
            // 客户端在服务器缓存的访问凭证：设备IP-用户名-用户空间 --->> Access-Token
            String clientPresentToken = clientDetails.getClientPresentToken();
            // 发送HTTP请求的客户端在服务器已登录的用户列表：设备IP --->> Map<用户空间标识, 用户名>
            Map<String, String> serverUserProfileMap = clientDetails.getServerUserProfileMap();
            // 当前用户已登录的设备列表：用户名 --->> Map<Access-Token, 设备IP>
            Map<String, String> serverCredentialsMap = clientDetails.getServerCredentialsMap();
            // 请求头携带的访问凭证
            String requestHeaderToken = clientDetails.getRequestHeaderToken();
            // 当前会话绑定的用户名
            String sessionPrincipal = clientDetails.getSessionPrincipal();
            // 当前会话绑定的客户端IP地址
            String sessionClientIpAddress = clientDetails.getSessionClientIpAddress();
            // 当前会话绑定的用户空间
            String sessionUserProfile = clientDetails.getSessionUserProfile();

            // token非空且格式错误
            if (StringUtils.isNotEmpty(requestHeaderToken) && !validSessionId(requestHeaderToken)) {
                throw new SystemInterrupterException(ExceptionStatusEnum.INVALID_TOKEN_IN_HTTP_HEADER);
            }

            if (StringUtils.isNotEmpty(sessionPrincipal) && StringUtils.isNotEmpty(sessionClientIpAddress) && StringUtils.isNotEmpty(sessionUserProfile)) { // 请求头携带的token未过期
                if (StringUtils.isNotEmpty(clientPresentToken)) { // 用户`clientLoginName`在终端`clientIpAddress`下的用户空间`clientUserProfile`已经登录
                    if (sessionClientIpAddress.equalsIgnoreCase(clientIpAddress)) { // 当前会话对象绑定的IP和请求客户端相同
                        if (sessionUserProfile.equalsIgnoreCase(clientUserProfile)) { // 用户空间相同
                            // 已经登录，验证通过，返回已有的会话对象标识
                            return requestHeaderToken;
                        } else { // 用户空间不一样
                            if (sessionPrincipal.equalsIgnoreCase(clientLoginName)) { // 使用自己的token在其它用户空间访问api
                                throw new SystemInterrupterException(ExceptionStatusEnum.ILLEGAL_MYSELF_PROFILE_TOKEN);
                            } else { // 使用别人的token在其它用户空间访问api
                                throw new SystemInterrupterException(ExceptionStatusEnum.ILLEGAL_OTHERS_PROFILE_TOKEN);
                            }
                        }
                    } else { // 当前会话对象绑定的IP和请求客户端不一样
                        throw new SystemInterrupterException(ExceptionStatusEnum.INCORRECT_DEVICE_FROM_CLIENT);
                    }
                } else { // 用户`clientLoginName`在终端`clientIpAddress`下的用户空间`clientUserProfile`没有登录
                    if (sessionClientIpAddress.equalsIgnoreCase(clientIpAddress)) {
                        if (serverUserProfileMap.keySet().contains(clientUserProfile)) { // 用户空间已经被占用
                            throw new SystemInterrupterException(ExceptionStatusEnum.OVERFLOW_CLIENT_LIMIT_SIZE);
                        } else { // 用户空间没有占用
                            throw new SystemInterrupterException(ExceptionStatusEnum.ILLEGAL_OTHERS_PROFILE_TOKEN);
                        }
                    } else { // 当前会话对象绑定的IP和请求客户端不一样
                        throw new SystemInterrupterException(ExceptionStatusEnum.INCORRECT_DEVICE_FROM_CLIENT);
                    }
                }
            } else { // 请求头携带的token无效
                if (StringUtils.isNotEmpty(clientPresentToken)) { // 用户`clientLoginName`在终端`clientIpAddress`下的用户空间`clientUserProfile`已经登录
                    throw new SystemInterrupterException(ExceptionStatusEnum.INCORRECT_TOKEN_FROM_CLIENT);
                } else { // 用户`clientLoginName`在终端`clientIpAddress`下的用户空间`clientUserProfile`没有登录
                    if (serverUserProfileMap.keySet().contains(clientUserProfile)) { // 用户空间已经被占用
                        throw new SystemInterrupterException(ExceptionStatusEnum.OVERFLOW_CLIENT_LIMIT_SIZE);
                    } else { // 用户空间没有占用
                        // 用户在当前设备没有登录，必须限制会话对象在不同设备的数量，相同设备可以在多个终端登录
                        if (new HashSet<>(serverCredentialsMap.values()).size() >= SessionConstant.CREDENTIALS_SIZE
                                // 这个条件允许用户在相同IP但用户空间不同的设备下可以有多个会话
                                && !serverCredentialsMap.values().contains(clientIpAddress)) {
                            throw new SystemInterrupterException(ExceptionStatusEnum.OVERFLOW_SESSION_LIMIT_SIZE);
                        } else {
                            // 没有登录，验证通过，返回一个空的字符串
                            return StringUtils.EMPTY;
                        }
                    }
                }
            }
        } catch (Exception e) {
            throw new UtilityException(e);
        }
    }

    /**
     * 验证token字符串是否为UUID的格式
     */
    public static boolean validSessionId(String accessToken) {
        try {
            UUID.fromString(accessToken);
            return true;
        } catch (Exception ignored) {
            return false;
        }
    }

}
